{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xLOXFOT5Q40E"
      },
      "source": [
        "##### Copyright 2020 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "iiQkM5ZgQ8r2"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "j6331ZSsQGY3"
      },
      "source": [
        "# 勾配の計算"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "i9Jcnb8bQQyd"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td><a target=\"_blank\" href=\"https://www.tensorflow.org/quantum/tutorials/gradients\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\"> TensorFlow.orgで表示</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ja/quantum/tutorials/gradients.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\"> Google Colab で実行</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ja/quantum/tutorials/gradients.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\">GitHub でソースを表示{</a></td>\n",
        "  <td><a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ja/quantum/tutorials/gradients.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\">ノートブックをダウンロード</a></td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "FxkQA6oblNqI"
      },
      "source": [
        "このチュートリアルでは、量子回路の期待値の勾配計算アルゴリズムについて説明します。\n",
        "\n",
        "量子回路で特定のオブザーバブルの期待値の勾配を計算することは、複雑なプロセスです。行列の乗算やベクトルの加算などの従来の機械学習変換では簡単に使用できる解析的勾配式がありますが、オブザーバブルの期待値には、このような解析的勾配式は必ずしもありません。そのため、シナリオに適したさまざまな量子勾配計算方法を使用する必要があります。このチュートリアルでは、2 つの異なる微分スキームを比較対照します。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "pvG0gAJqGYJo"
      },
      "source": [
        "## セットアップ"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "TorxE5tnkvb2"
      },
      "outputs": [],
      "source": [
        "!pip install tensorflow==2.1.0"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "OIbP5hklC338"
      },
      "source": [
        "TensorFlow Quantum をインストールします。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "saFHsRDpkvkH"
      },
      "outputs": [],
      "source": [
        "!pip install tensorflow-quantum"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "MkTqyoSxGUfB"
      },
      "source": [
        "次に、TensorFlow とモジュールの依存関係をインポートします。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "enZ300Bflq80"
      },
      "outputs": [],
      "source": [
        "import tensorflow as tf\n",
        "import tensorflow_quantum as tfq\n",
        "\n",
        "import cirq\n",
        "import sympy\n",
        "import numpy as np\n",
        "\n",
        "# visualization tools\n",
        "%matplotlib inline\n",
        "import matplotlib.pyplot as plt\n",
        "from cirq.contrib.svg import SVGCircuit"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "b08Mmbs8lr81"
      },
      "source": [
        "## 1. 予備\n",
        "\n",
        "量子回路の勾配計算の概念をもう少し具体的に見てみましょう。次のようなパラメータ化された回路があるとします。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "YkPYJ_Ak-GKu"
      },
      "outputs": [],
      "source": [
        "qubit = cirq.GridQubit(0, 0)\n",
        "my_circuit = cirq.Circuit(cirq.Y(qubit)**sympy.Symbol('alpha'))\n",
        "SVGCircuit(my_circuit)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wgQIlCWy-MVr"
      },
      "source": [
        "オブザーバブルは以下のとおりです。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "xurmJdFy-Jae"
      },
      "outputs": [],
      "source": [
        "pauli_x = cirq.X(qubit)\n",
        "pauli_x"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "j3OzKYe5NT_W"
      },
      "source": [
        "この演算子を見ると、$⟨Y(\\alpha)| X | Y(\\alpha)⟩ = \\sin(\\pi \\alpha)$ であることが分かります。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Ps-pd2mndXs7"
      },
      "outputs": [],
      "source": [
        "def my_expectation(op, alpha):\n",
        "    \"\"\"Compute ⟨Y(alpha)| `op` | Y(alpha)⟩\"\"\"\n",
        "    params = {'alpha': alpha}\n",
        "    sim = cirq.Simulator()\n",
        "    final_state = sim.simulate(my_circuit, params).final_state\n",
        "    return op.expectation_from_wavefunction(final_state, {qubit: 0}).real\n",
        "\n",
        "\n",
        "my_alpha = 0.3\n",
        "print(\"Expectation=\", my_expectation(pauli_x, my_alpha))\n",
        "print(\"Sin Formula=\", np.sin(np.pi * my_alpha))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "zcCX109cJUaz"
      },
      "source": [
        "$f_{1}(\\alpha) = ⟨Y(\\alpha)| X | Y(\\alpha)⟩$ と定義すると $f_{1}^{'}(\\alpha) = \\pi \\cos(\\pi \\alpha)$ になります。確認しましょう。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "VMq7EayNRyQb"
      },
      "outputs": [],
      "source": [
        "def my_grad(obs, alpha, eps=0.01):\n",
        "    grad = 0\n",
        "    f_x = my_expectation(obs, alpha)\n",
        "    f_x_prime = my_expectation(obs, alpha + eps)\n",
        "    return ((f_x_prime - f_x) / eps).real\n",
        "\n",
        "\n",
        "print('Finite difference:', my_grad(pauli_x, my_alpha))\n",
        "print('Cosine formula:   ', np.pi * np.cos(np.pi * my_alpha))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-SUlLpXBeicF"
      },
      "source": [
        "## 2. 微分器の必要性\n",
        "\n",
        "より大きな回路では、与えられた量子回路の勾配を正確に計算する式は必ずしもありません。単純な式では勾配を計算できない場合、`tfq.differentiators.Differentiator`クラスを使用すると、回路の勾配を計算するためのアルゴリズムを定義できます。たとえば、TensorFlow Quantum（TFQ）で上記の例を次のように再現できます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Om76ZLu8NT_i"
      },
      "outputs": [],
      "source": [
        "expectation_calculation = tfq.layers.Expectation(\n",
        "    differentiator=tfq.differentiators.ForwardDifference(grid_spacing=0.01))\n",
        "\n",
        "expectation_calculation(my_circuit,\n",
        "                        operators=pauli_x,\n",
        "                        symbol_names=['alpha'],\n",
        "                        symbol_values=[[my_alpha]])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lx3y2DX9NT_k"
      },
      "source": [
        "ただし、サンプリングに基づいて期待値を推定するように切り替えると（実際のデバイスで何が起こるか）、値が少し変わる可能性があります。これは、期待値が不正確になることを意味します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "v27rRyAHNT_l"
      },
      "outputs": [],
      "source": [
        "sampled_expectation_calculation = tfq.layers.SampledExpectation(\n",
        "    differentiator=tfq.differentiators.ForwardDifference(grid_spacing=0.01))\n",
        "\n",
        "sampled_expectation_calculation(my_circuit,\n",
        "                                operators=pauli_x,\n",
        "                                repetitions=500,\n",
        "                                symbol_names=['alpha'],\n",
        "                                symbol_values=[[my_alpha]])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Igwa3EnzNT_p"
      },
      "source": [
        "これは、勾配における深刻な精度の問題につながる可能性があります。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "StljXH38NT_q"
      },
      "outputs": [],
      "source": [
        "# Make input_points = [batch_size, 1] array.\n",
        "input_points = np.linspace(0, 5, 200)[:, np.newaxis].astype(np.float32)\n",
        "exact_outputs = expectation_calculation(my_circuit,\n",
        "                                        operators=pauli_x,\n",
        "                                        symbol_names=['alpha'],\n",
        "                                        symbol_values=input_points)\n",
        "imperfect_outputs = sampled_expectation_calculation(my_circuit,\n",
        "                                                    operators=pauli_x,\n",
        "                                                    repetitions=500,\n",
        "                                                    symbol_names=['alpha'],\n",
        "                                                    symbol_values=input_points)\n",
        "plt.title('Forward Pass Values')\n",
        "plt.xlabel('$x$')\n",
        "plt.ylabel('$f(x)$')\n",
        "plt.plot(input_points, exact_outputs, label='Analytic')\n",
        "plt.plot(input_points, imperfect_outputs, label='Sampled')\n",
        "plt.legend()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "dfXObk7KNT_t"
      },
      "outputs": [],
      "source": [
        "# Gradients are a much different story.\n",
        "values_tensor = tf.convert_to_tensor(input_points)\n",
        "\n",
        "with tf.GradientTape() as g:\n",
        "    g.watch(values_tensor)\n",
        "    exact_outputs = expectation_calculation(my_circuit,\n",
        "                                            operators=pauli_x,\n",
        "                                            symbol_names=['alpha'],\n",
        "                                            symbol_values=values_tensor)\n",
        "analytic_finite_diff_gradients = g.gradient(exact_outputs, values_tensor)\n",
        "\n",
        "with tf.GradientTape() as g:\n",
        "    g.watch(values_tensor)\n",
        "    imperfect_outputs = sampled_expectation_calculation(\n",
        "        my_circuit,\n",
        "        operators=pauli_x,\n",
        "        repetitions=500,\n",
        "        symbol_names=['alpha'],\n",
        "        symbol_values=values_tensor)\n",
        "sampled_finite_diff_gradients = g.gradient(imperfect_outputs, values_tensor)\n",
        "\n",
        "plt.title('Gradient Values')\n",
        "plt.xlabel('$x$')\n",
        "plt.ylabel('$f^{\\'}(x)$')\n",
        "plt.plot(input_points, analytic_finite_diff_gradients, label='Analytic')\n",
        "plt.plot(input_points, sampled_finite_diff_gradients, label='Sampled')\n",
        "plt.legend()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Ld34TJvTNT_w"
      },
      "source": [
        "ここでは、解析の場合は有限差分式は勾配自体を高速に計算できますが、サンプリングベースの方法の場合ではノイズが多すぎることが分かります。適切な勾配を計算するには、より注意深い手法を使用する必要があります。次に、解析的期待値の勾配計算にはあまり適していませんが、実際のサンプルベースの方法の場合ではより優れたパフォーマンスを発揮する、大幅に低速な手法を見ていきます。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "JsBxH_RaNT_x"
      },
      "outputs": [],
      "source": [
        "# A smarter differentiation scheme.\n",
        "gradient_safe_sampled_expectation = tfq.layers.SampledExpectation(\n",
        "    differentiator=tfq.differentiators.ParameterShift())\n",
        "\n",
        "with tf.GradientTape() as g:\n",
        "    g.watch(values_tensor)\n",
        "    imperfect_outputs = gradient_safe_sampled_expectation(\n",
        "        my_circuit,\n",
        "        operators=pauli_x,\n",
        "        repetitions=500,\n",
        "        symbol_names=['alpha'],\n",
        "        symbol_values=values_tensor)\n",
        "\n",
        "sampled_param_shift_gradients = g.gradient(imperfect_outputs, values_tensor)\n",
        "\n",
        "plt.title('Gradient Values')\n",
        "plt.xlabel('$x$')\n",
        "plt.ylabel('$f^{\\'}(x)$')\n",
        "plt.plot(input_points, analytic_finite_diff_gradients, label='Analytic')\n",
        "plt.plot(input_points, sampled_param_shift_gradients, label='Sampled')\n",
        "plt.legend()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0xlUlh8wNT_z"
      },
      "source": [
        "上記から、特定の微分器が特定の研究シナリオに最適であることがわかります。一般に、デバイスノイズなどに対して堅牢な、低速のサンプルベースの方法は、より「現実的」設定でアルゴリズムをテストまたは実装する場合に適した微分器です。有限差分のようなより高速な方法はアルゴリズムのデバイスにおける実行可能性にはまだ関心がなく、解析的計算やより高いスループットが必要な場合に最適です。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "FaijzZ4MNT_0"
      },
      "source": [
        "## 3. 複数のオブザーバブル\n",
        "\n",
        "2 番目のオブザーバブルを使用し、TensorFlow Quantum が 1 つの回路に対して複数のオブザーバブルをサポートする方法を見てみましょう。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ytgB_DqDNT_3"
      },
      "outputs": [],
      "source": [
        "pauli_z = cirq.Z(qubit)\n",
        "pauli_z"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "r51TZls4NT_6"
      },
      "source": [
        "このオブザーバブルが以前と同じ回路で使用されている場合、$f_{2}(\\alpha) = ⟨Y(\\alpha)| Z | Y(\\alpha)⟩ = \\cos(\\pi \\alpha)$ および$f_{2}^{'}(\\alpha) = -\\pi \\sin(\\pi \\alpha)$になります。確認します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "19FKgu0ANT_7"
      },
      "outputs": [],
      "source": [
        "test_value = 0.\n",
        "\n",
        "print('Finite difference:', my_grad(pauli_z, test_value))\n",
        "print('Sin formula:      ', -np.pi * np.sin(np.pi * test_value))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "_33Y5mL0NT_-"
      },
      "source": [
        "（ほぼ）一致します。\n",
        "\n",
        "次に、$g(\\alpha) = f_{1}(\\alpha) + f_{2}(\\alpha)$ を定義すると、$g'(\\alpha) = f_{1}^{'}(\\alpha) + f^{'}_{2}(\\alpha)$になります。TensorFlow Quantum で複数のオブザーバブルを定義して回路と共に使用するには、$g$ にさらに項を追加します。\n",
        "\n",
        "これは、回路内の特定のシンボルの勾配が、その回路に適用されたそのシンボルの各オブザーバブルに関する勾配の合計に等しいことを意味します。これは、TensorFlow の勾配取得およびバックプロパゲーションと互換性があります（特定のシンボルの勾配として、すべてのオブザーバブルの勾配の合計を指定します）。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "3WFJfFEbNT_-"
      },
      "outputs": [],
      "source": [
        "sum_of_outputs = tfq.layers.Expectation(\n",
        "    differentiator=tfq.differentiators.ForwardDifference(grid_spacing=0.01))\n",
        "\n",
        "sum_of_outputs(my_circuit,\n",
        "               operators=[pauli_x, pauli_z],\n",
        "               symbol_names=['alpha'],\n",
        "               symbol_values=[[test_value]])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-ujQUu3WNUAB"
      },
      "source": [
        "ここで、最初のエントリは期待値 w.r.t Pauli X であり、2 番目のエントリは期待値 w.r.t Pauli Z です。勾配は以下のとおりです。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "jcAQa9l0NUAB"
      },
      "outputs": [],
      "source": [
        "test_value_tensor = tf.convert_to_tensor([[test_value]])\n",
        "\n",
        "with tf.GradientTape() as g:\n",
        "    g.watch(test_value_tensor)\n",
        "    outputs = sum_of_outputs(my_circuit,\n",
        "                             operators=[pauli_x, pauli_z],\n",
        "                             symbol_names=['alpha'],\n",
        "                             symbol_values=test_value_tensor)\n",
        "\n",
        "sum_of_gradients = g.gradient(outputs, test_value_tensor)\n",
        "\n",
        "print(my_grad(pauli_x, test_value) + my_grad(pauli_z, test_value))\n",
        "print(sum_of_gradients.numpy())"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-fZmbYGANUAE"
      },
      "source": [
        "ここで、各オブザーバブルの勾配の合計が実際に $\\alpha$ の勾配であることを確認しました。この動作は、すべての TensorFlow Quantum 微分器によってサポートされており、TensorFlow の他の部分との互換性において重要な役割を果たします。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lZsGG7lWNUAF"
      },
      "source": [
        "## 4. 高度な使用\n",
        "\n",
        "ここでは、量子回路用に独自のカスタム微分ルーチンを定義する方法を紹介します。TensorFlowQuantum サブクラス`tfq.differentiators.Differentiator`内に存在するすべての微分器。微分器は`differentiate_analytic`と`differentiate_sampled`を実装する必要があります。\n",
        "\n",
        "以下では、TensorFlow Quantum 構造を使用して、このチュートリアルの最初の部分の閉形式の解を実装します。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "5iY4q6FKNUAG"
      },
      "outputs": [],
      "source": [
        "class MyDifferentiator(tfq.differentiators.Differentiator):\n",
        "    \"\"\"A Toy differentiator for <Y^alpha | X |Y^alpha>.\"\"\"\n",
        "\n",
        "    def __init__(self):\n",
        "        pass\n",
        "\n",
        "    @tf.function\n",
        "    def _compute_gradient(self, symbol_values):\n",
        "        \"\"\"Compute the gradient based on symbol_values.\"\"\"\n",
        "\n",
        "        # f(x) = sin(pi * x)\n",
        "        # f'(x) = pi * cos(pi * x)\n",
        "        return tf.cast(tf.cos(symbol_values * np.pi) * np.pi, tf.float32)\n",
        "\n",
        "    @tf.function\n",
        "    def differentiate_analytic(self, programs, symbol_names, symbol_values,\n",
        "                               pauli_sums, forward_pass_vals, grad):\n",
        "        \"\"\"Specify how to differentiate a circuit with analytical expectation.\n",
        "\n",
        "        This is called at graph runtime by TensorFlow. `differentiate_analytic`\n",
        "        should calculate the gradient of a batch of circuits and return it\n",
        "        formatted as indicated below. See\n",
        "        `tfq.differentiators.ForwardDifference` for an example.\n",
        "\n",
        "        Args:\n",
        "            programs: `tf.Tensor` of strings with shape [batch_size] containing\n",
        "                the string representations of the circuits to be executed.\n",
        "            symbol_names: `tf.Tensor` of strings with shape [n_params], which\n",
        "                is used to specify the order in which the values in\n",
        "                `symbol_values` should be placed inside of the circuits in\n",
        "                `programs`.\n",
        "            symbol_values: `tf.Tensor` of real numbers with shape\n",
        "                [batch_size, n_params] specifying parameter values to resolve\n",
        "                into the circuits specified by programs, following the ordering\n",
        "                dictated by `symbol_names`.\n",
        "            pauli_sums: `tf.Tensor` of strings with shape [batch_size, n_ops]\n",
        "                containing the string representation of the operators that will\n",
        "                be used on all of the circuits in the expectation calculations.\n",
        "            forward_pass_vals: `tf.Tensor` of real numbers with shape\n",
        "                [batch_size, n_ops] containing the output of the forward pass\n",
        "                through the op you are differentiating.\n",
        "            grad: `tf.Tensor` of real numbers with shape [batch_size, n_ops]\n",
        "                representing the gradient backpropagated to the output of the\n",
        "                op you are differentiating through.\n",
        "\n",
        "        Returns:\n",
        "            A `tf.Tensor` with the same shape as `symbol_values` representing\n",
        "            the gradient backpropagated to the `symbol_values` input of the op\n",
        "            you are differentiating through.\n",
        "        \"\"\"\n",
        "\n",
        "        # Computing gradients just based off of symbol_values.\n",
        "        return self._compute_gradient(symbol_values) * grad\n",
        "\n",
        "    @tf.function\n",
        "    def differentiate_sampled(self, programs, symbol_names, symbol_values,\n",
        "                              pauli_sums, num_samples, forward_pass_vals, grad):\n",
        "        \"\"\"Specify how to differentiate a circuit with sampled expectation.\n",
        "\n",
        "        This is called at graph runtime by TensorFlow. `differentiate_sampled`\n",
        "        should calculate the gradient of a batch of circuits and return it\n",
        "        formatted as indicated below. See\n",
        "        `tfq.differentiators.ForwardDifference` for an example.\n",
        "\n",
        "        Args:\n",
        "            programs: `tf.Tensor` of strings with shape [batch_size] containing\n",
        "                the string representations of the circuits to be executed.\n",
        "            symbol_names: `tf.Tensor` of strings with shape [n_params], which\n",
        "                is used to specify the order in which the values in\n",
        "                `symbol_values` should be placed inside of the circuits in\n",
        "                `programs`.\n",
        "            symbol_values: `tf.Tensor` of real numbers with shape\n",
        "                [batch_size, n_params] specifying parameter values to resolve\n",
        "                into the circuits specified by programs, following the ordering\n",
        "                dictated by `symbol_names`.\n",
        "            pauli_sums: `tf.Tensor` of strings with shape [batch_size, n_ops]\n",
        "                containing the string representation of the operators that will\n",
        "                be used on all of the circuits in the expectation calculations.\n",
        "            num_samples: `tf.Tensor` of positive integers representing the\n",
        "                number of samples per term in each term of pauli_sums used\n",
        "                during the forward pass.\n",
        "            forward_pass_vals: `tf.Tensor` of real numbers with shape\n",
        "                [batch_size, n_ops] containing the output of the forward pass\n",
        "                through the op you are differentiating.\n",
        "            grad: `tf.Tensor` of real numbers with shape [batch_size, n_ops]\n",
        "                representing the gradient backpropagated to the output of the\n",
        "                op you are differentiating through.\n",
        "\n",
        "        Returns:\n",
        "            A `tf.Tensor` with the same shape as `symbol_values` representing\n",
        "            the gradient backpropagated to the `symbol_values` input of the op\n",
        "            you are differentiating through.\n",
        "        \"\"\"\n",
        "        return self._compute_gradient(symbol_values) * grad"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "bvEgw2m6NUAI"
      },
      "source": [
        "この新しい微分器は、既存の`tfq.layer`オブジェクトで使用できるようになりました。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "QrKnkWswNUAJ"
      },
      "outputs": [],
      "source": [
        "custom_dif = MyDifferentiator()\n",
        "custom_grad_expectation = tfq.layers.Expectation(differentiator=custom_dif)\n",
        "\n",
        "# Now let's get the gradients with finite diff.\n",
        "with tf.GradientTape() as g:\n",
        "    g.watch(values_tensor)\n",
        "    exact_outputs = expectation_calculation(my_circuit,\n",
        "                                            operators=[pauli_x],\n",
        "                                            symbol_names=['alpha'],\n",
        "                                            symbol_values=values_tensor)\n",
        "\n",
        "analytic_finite_diff_gradients = g.gradient(exact_outputs, values_tensor)\n",
        "\n",
        "# Now let's get the gradients with custom diff.\n",
        "with tf.GradientTape() as g:\n",
        "    g.watch(values_tensor)\n",
        "    my_outputs = custom_grad_expectation(my_circuit,\n",
        "                                         operators=[pauli_x],\n",
        "                                         symbol_names=['alpha'],\n",
        "                                         symbol_values=values_tensor)\n",
        "\n",
        "my_gradients = g.gradient(my_outputs, values_tensor)\n",
        "\n",
        "plt.subplot(1, 2, 1)\n",
        "plt.title('Exact Gradient')\n",
        "plt.plot(input_points, analytic_finite_diff_gradients.numpy())\n",
        "plt.xlabel('x')\n",
        "plt.ylabel('f(x)')\n",
        "plt.subplot(1, 2, 2)\n",
        "plt.title('My Gradient')\n",
        "plt.plot(input_points, my_gradients.numpy())\n",
        "plt.xlabel('x')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "oXqcJWigNUAL"
      },
      "source": [
        "この新しい微分器を使用して、微分可能な演算を生成できるようになりました。\n",
        "\n",
        "重要点：微分器は一度に 1 つの演算にしか接続できないため、以前に演算に接続されていた微分器は、新しい演算に接続する前に更新する必要があります。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "F_WHcj3bNUAM"
      },
      "outputs": [],
      "source": [
        "# Create a noisy sample based expectation op.\n",
        "expectation_sampled = tfq.get_sampled_expectation_op(\n",
        "    cirq.DensityMatrixSimulator(noise=cirq.depolarize(0.01)))\n",
        "\n",
        "# Make it differentiable with your differentiator:\n",
        "# Remember to refresh the differentiator before attaching the new op\n",
        "custom_dif.refresh()\n",
        "differentiable_op = custom_dif.generate_differentiable_op(\n",
        "    sampled_op=expectation_sampled)\n",
        "\n",
        "# Prep op inputs.\n",
        "circuit_tensor = tfq.convert_to_tensor([my_circuit])\n",
        "op_tensor = tfq.convert_to_tensor([[pauli_x]])\n",
        "single_value = tf.convert_to_tensor([[my_alpha]])\n",
        "num_samples_tensor = tf.convert_to_tensor([[1000]])\n",
        "\n",
        "with tf.GradientTape() as g:\n",
        "    g.watch(single_value)\n",
        "    forward_output = differentiable_op(circuit_tensor, ['alpha'], single_value,\n",
        "                                       op_tensor, num_samples_tensor)\n",
        "\n",
        "my_gradients = g.gradient(forward_output, single_value)\n",
        "\n",
        "print('---TFQ---')\n",
        "print('Foward:  ', forward_output.numpy())\n",
        "print('Gradient:', my_gradients.numpy())\n",
        "print('---Original---')\n",
        "print('Forward: ', my_expectation(pauli_x, my_alpha))\n",
        "print('Gradient:', my_grad(pauli_x, my_alpha))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "OGWcpqzDNUAP"
      },
      "source": [
        "成功：TensorFlow Quantum が提供するすべての微分器を使用して、独自の微分器を定義できるようになりました。"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "gradients.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
